﻿using mozhi.smarterp.Business.Purchases.Order;
using mozhi.smarterp.Business.Purchases.ProductPrices;
using mozhi.smarterp.Business.Purchases.Repositories;
using mozhi.smarterp.Business.Purchases.Services;
using Mozhi.Abp.Domain.BasicData;
using Mozhi.Abp.Domain.BasicData.Repositories;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;

namespace mozhi.smarterp.Business.Purchases.ProductPrice
{
    public class PurchaseProductPriceAppService : BusinessAppService, IPurchaseProductPriceAppService
    {
        private IProductRepository _productRepository;
        private ICurrencyRepository _currencyRepository;
        private ITaxRateRepository _taxRateRepository;
        private IUnitRepository _unitRepository;
        private ISupplierRepository _supplierRepository;
        private IPurchaseProductPriceRepository _repository;
        private PurchaseProductPriceManager _manager;

        public PurchaseProductPriceAppService(IProductRepository productRepository, ICurrencyRepository currencyRepository, ITaxRateRepository taxRateRepository, 
            IUnitRepository unitRepository, ISupplierRepository supplierRepository, IPurchaseProductPriceRepository repository, PurchaseProductPriceManager manager)
        {
            _productRepository = productRepository;
            _currencyRepository = currencyRepository;
            _taxRateRepository = taxRateRepository;
            _unitRepository = unitRepository;
            _supplierRepository = supplierRepository;
            _repository = repository;
            _manager = manager;
        }

        public async Task<PurchaseProductPriceDto> CreateAsync(PurchaseProductPriceCreateDto input)
        {
            var productPrice = await _manager.GetAsync(null, input.Code, input.Name, input.SupplierId, input.CurrencyId, input.IncludeTax);

            input.Items = input.Items ?? new List<PurchaseProductPriceItemCreateDto>();

            var productPriceItems = new List<PurchaseProductPriceItem>();

            foreach (var item in input.Items)
            {
                var productPriceItem = await _manager.GetItemAsync(null, item.ProductId, item.Price, item.MinQuantity, item.MaxQuantity, item.TaxRateId, item.EffectiveDate, item.ExpirationDate);

                productPriceItems.Add(productPriceItem);
            }

            productPrice.SetItems(productPriceItems);

            productPrice = await _repository.InsertAsync(productPrice);

            var result = ObjectMapper.Map<PurchaseProductPrice , PurchaseProductPriceDto>(productPrice);

            await LoadOtherInfoAysnc(new List<PurchaseProductPriceDto> { result });

            return result;
        }

        public async Task DeleteAsync(Guid id)
        {
            await _repository.DeleteAsync(id);
        }

        public async Task<PurchaseProductPriceDto> GetAsync(Guid id)
        {
            var productPrice = await _repository.GetAsync(id);

            var result = ObjectMapper.Map<PurchaseProductPrice, PurchaseProductPriceDto>(productPrice);

            await LoadOtherInfoAysnc(new List<PurchaseProductPriceDto> { result });

            return result;
        }

        public async Task<PagedResultDto<PurchaseProductPriceDto>> GetListAsync(PurchaseProductPricePagedResultRequestDto filter)
        {
            var productPrices = await _repository.GetPagedListAsync(filter.Keyword, null, null, filter.SupplierId, filter.CurrencyId, filter.SkipCount, filter.MaxResultCount, filter.Sorting ,filter.IncludeDetails);

            var count = await _repository.GetCountAsync(filter.Keyword, null, null, filter.SupplierId, filter.CurrencyId , null);

            var items = ObjectMapper.Map<List<PurchaseProductPrice>, List<PurchaseProductPriceDto>>(productPrices);

            await LoadOtherInfoAysnc(items);

            return new PagedResultDto<PurchaseProductPriceDto>()
            {
                Items = items,
                TotalCount = count
            };
        }

        public async Task<List<PurchaseProductPriceResultDto>> GetListAsync(PurchaseProductPriceResultRequestDto filter)
        {
            var productIds = filter.ProductItems.Select(x => x.ProductId).ToList();

            var productPrices = await _repository.GetListAsync(filter.SupplierId, filter.CurrencyId, filter.IncludeTax, filter.Date, productIds);

            var result = new List<PurchaseProductPriceResultDto>();

            if (productPrices.Count == 0)
                return result;

            foreach (var productPrice in productPrices)
            {
                var items = productPrice.Items;

                if (items.Count == 0)
                    continue;

                var matchItems = items.Where(x => productIds.Contains(x.ProductId)).ToList();

                if (matchItems.Count == 0)
                    continue;

                foreach (var item in matchItems)
                {
                    var matchProductFilters = filter.ProductItems.Where(x => x.ProductId == item.ProductId).ToList();

                    if (matchProductFilters.Count == 0)
                        continue;

                    var productPriceResults = GetProductPriceResults(productPrice, item, matchProductFilters);

                    result.AddRange(productPriceResults);
                }
            }
            if (result.Count == 0)
                return result;

            //获取完结果以后，返回的数据，每个产品只能包含一条数据，逻辑：有供应商的，优先取供应商，相同供应商的，优先取价目表code最大值
            var groups = result.GroupBy(x => x.SupplierId);

            var finalDatas = new List<PurchaseProductPriceResultDto>();

            foreach (var group in groups)
            {
                var groupItems = group.ToList();

                var match = groupItems.Exists(x => x.SupplierId.HasValue) ? groupItems.Where(x => x.SupplierId.HasValue).OrderBy(x => x.Code).First() : groupItems.OrderBy(x => x.Code).First();

                finalDatas.Add(match);
            }

            return finalDatas;
        }

        public async Task<PurchaseProductPriceDto> UpdateAsync(PurchaseProductPriceUpdateDto input)
        {
            var productPrice = await _manager.GetAsync(input.Id, input.Code, input.Name, input.SupplierId, input.CurrencyId, input.IncludeTax);

            input.Items = input.Items ?? new List<PurchaseProductPriceItemUpdateDto>();

            var productPriceItems = new List<PurchaseProductPriceItem>();

            foreach (var item in input.Items)
            {
                var productPriceItem = await _manager.GetItemAsync(item.Id, item.ProductId, item.Price, item.MinQuantity, item.MaxQuantity, item.TaxRateId, item.EffectiveDate, item.ExpirationDate);

                productPriceItems.Add(productPriceItem);
            }

            productPrice.SetItems(productPriceItems);

            productPrice = await _repository.UpdateAsync(productPrice);

            var result = ObjectMapper.Map<PurchaseProductPrice, PurchaseProductPriceDto>(productPrice);

            await LoadOtherInfoAysnc(new List<PurchaseProductPriceDto> { result });

            return result;
        }

        #region
        private async Task LoadOtherInfoAysnc(List<PurchaseProductPriceDto> productPrices)
        {
            if (productPrices.IsNullOrEmpty())
                return;

            foreach (var productPrice in productPrices)
            {
                var supplier = productPrice.SupplierId.HasValue ? await _supplierRepository.GetAsync(productPrice.SupplierId.Value) : null;

                var currency = await _currencyRepository.GetAsync(productPrice.CurrencyId);

                productPrice.SupplierName = supplier?.Name;
                productPrice.CurrencyName = currency?.Name;

                foreach (var item in productPrice.Items)
                {
                    var product = await _productRepository.GetAsync(item.ProductId);
                    var basicUnit = await _unitRepository.GetAsync(item.BasicUnitId);
                    var taxRate = item.TaxRateId.HasValue ? await _taxRateRepository.GetAsync(item.TaxRateId.Value) : null;

                    item.ProductCode = product?.Code;
                    item.ProductName = product?.Name;
                    item.ProductModel = product?.Model;
                    item.BasicUnitName = basicUnit?.Name;
                    item.TaxRateName = taxRate?.Name;
                }
            }
        }

        private List<PurchaseProductPriceResultDto> GetProductPriceResults(PurchaseProductPrice productPrice , PurchaseProductPriceItem productPriceItem, List<PurchaseProductPriceItemResultRequestDto> filters)
        {
            if (filters == null)
                throw new ArgumentNullException(nameof(filters));

            var datas = new List<PurchaseProductPriceResultDto>();

            foreach (var filter in filters)
            {
                var result = GetProductPriceResult(productPriceItem, filter);

                if (result != null)
                    datas.Add(result);
            }

            datas.ForEach(data =>
            {
                data.Code = productPrice.Code;
                data.SupplierId = productPrice.SupplierId;
                data.CurrencyId = productPrice.CurrencyId;
                data.IncludeTax = productPrice.IncludeTax;
            });

            return datas;
        }

        private PurchaseProductPriceResultDto GetProductPriceResult(PurchaseProductPriceItem item , PurchaseProductPriceItemResultRequestDto filter)
        {
            //不在最小区间内
            if (item.MinQuantity != null && item.MinQuantity > filter.BasicQuantity)
                return null;

            //超过了最大区间
            if (item.MaxQuantity != null && item.MaxQuantity < filter.BasicQuantity)
                return null;

            return new PurchaseProductPriceResultDto()
            {
                ProductId = item.ProductId,
                Price = item.Price,
                EffectiveDate = item.EffectiveDate,
                ExpirationDate = item.ExpirationDate,
            };
        }
        #endregion
    }
}
